local function oncurrentexp(self, currentexp)
	if currentexp >= self.levelexp and self.currentlevel < self.maxlevel then
		currentexp = currentexp - self.levelexp
		self.currentlevel = self.currentlevel + 1
		if self.onupdatafn then
			self.onupdatafn(self.inst, true)
		end
	end
	if self.extraexp and currentexp >= self.extraexp then
		self.extrafn(self.inst)
	end
end

local function oncurrentlevel(self, currentlevel)
	self.levelexp = currentlevel * 10
end

local function Random_EXP()
	local lucky = math.random()
	if lucky < 0.12 then
		return 8
	elseif lucky < 0.2 then
		return 5
	elseif lucky < 0.3 then
		return 4
	elseif lucky < 1 then
		return 1
	end
end

local function ForgeLab(inst)
	local x, y, z = inst.Transform:GetWorldPosition()
	local ents = TheSim:FindEntities(x, y, z, 6, {"musha_forge"})
	if not (#ents) then
		return false
	end
	for k, v in pairs(ents) do
		if v.is_On and v.components.fueled.currentfuel >= 3 then
			v.Player_Upgrade()
			return true
		end
	end
	return false
end

local Musha_Equipment =
	Class(
	function(self, inst)
		self.inst = inst
		self.levelexp = 0
		self.maxlevel = 30
		self.currentlevel = 1
		self.currentexp = 0
		self.upgradable = false
		self.expchance = nil
		self.period = 0.1
		self.rate = 0
		--self.fueltype = "MUSHAFUEL"
		self.repairable = true
		inst:AddComponent("inspectable")
		inst:AddComponent("waterproofer")
		inst.components.waterproofer:SetEffectiveness(0)
		inst:AddComponent("inventoryitem")
		if not MUSHA_SETTINGS.share_item then
			inst:ListenForEvent(
				"equipped",
				function(inst, data)
					local owner = data.owner
					if owner and not (owner:HasTag("player") and owner:HasTag("_musha")) and owner.components.inventory then
						owner.components.inventory:Unequip(inst.components.equippable.equipslot, true)
						owner:DoTaskInTime(
							0,
							function()
								owner.components.inventory:DropItem(inst)
							end
						)
					end
				end
			)
		end
		inst:DoTaskInTime(
			0,
			function(inst)
				self.onupdatafn(inst)
			end
		)
	end,
	nil,
	{
		currentexp = oncurrentexp,
		currentlevel = oncurrentlevel
	}
)

function Musha_Equipment:TakeFuelItem(doer, item)
	if not item.components.musha_fuel then
		return
	end

	self:DoDelta(item.components.musha_fuel.repair_value)

	if item.components.stackable ~= nil then
		item.components.stackable:Get():Remove()
	else
		item:Remove()
	end

	if self.currentlevel < self.maxlevel or self.extraexp then
		self:Gain_Exp_From_Fuel(self.inst, item)
	else
		self.inst.components.talker:Say(STRINGS.MUSHA_LEVEL_INFORMATION)
	end
	if self.ontakefuelfn then
		self.ontakefuelfn(self.inst)
	end
end

function Musha_Equipment:SetTakeFuelFn(fn)
	self.ontakefuelfn = fn
end

function Musha_Equipment:SetOnBrokenFn(fn)
	if self.inst.components.finiteuses then
		self.inst.components.finiteuses:SetOnFinished(fn)
	elseif self.inst.components.armor then
		self.inst.components.armor.onbrokenfn = fn
	end
end

function Musha_Equipment:SetOnUpdataFn(fn)
	self.onupdatafn = fn
end

local function OnDoUpdate(inst, self, period)
	self:DoUpdate(period)
end

function Musha_Equipment:StartConsuming()
	self.consuming = true
	if self.consuming_task == nil then
		self.consuming_task = self.inst:DoPeriodicTask(self.period, OnDoUpdate, nil, self, self.period)
	end
end

function Musha_Equipment:DoUpdate(dt)
	if self.consuming then
		self:DoDelta(-dt * self.rate)
	end

	if self:IsBroken() then
		self:StopConsuming()
	end
end

function Musha_Equipment:StopConsuming()
	self.consuming = false
	if self.consuming_task ~= nil then
		self.consuming_task:Cancel()
		self.consuming_task = nil
	end
end

function Musha_Equipment:SetUpgradable(upgradable)
	self.upgradable = upgradable
	if upgradable then
		self.inst:ListenForEvent(
			"ondropped",
			function(inst)
				inst.owner = nil
			end
		)
		self.inst:ListenForEvent(
			"onpickup",
			function(inst, data)
				inst.owner = data.owner
			end
		)
		self.inst:ListenForEvent(
			"onremove",
			function(inst)
				if inst.owner.components.musha_equipbuff then
					inst.owner.components.musha_equipbuff.costing[inst.prefab] = self.currentexp
				end
			end
		)
	end
end

function Musha_Equipment:SetFuelRate(rate)
	self.rate = rate or 0
end

function Musha_Equipment:Gain_Exp(exp)
	self.currentexp = self.currentexp + exp
end

function Musha_Equipment:Gain_Exp_From_Fuel(inst, fuel)
	local exp
	if fuel.components.musha_fuel.exp_value and fuel.components.musha_fuel.exp_chance then
		exp = math.random() < fuel.components.musha_fuel.exp_chance and fuel.components.musha_fuel.exp_value or 0
	else
		exp = Random_EXP()
	end

	if ForgeLab(inst) then
		exp = exp * 5
		self:Gain_Exp(exp)
		inst.SoundEmitter:PlaySound("dontstarve/common/fireAddFuel")
		inst.components.talker:Say(
			STRINGS.MUSHA_TALK_FORGE_LUCKY ..
				"\n+(" .. exp .. ")\n[" .. STRINGS.MUSHA_ITEM_GROWPOINTS .. "]" .. self.currentexp .. "/" .. self.levelexp
		)
	else
		self:Gain_Exp(exp)
		if exp == 1 then
			inst.components.talker:Say(
				inst:GetDisplayName() ..
					" Lv" ..
						self.currentlevel .. "\n" .. STRINGS.MUSHA_ITEM_GROWPOINTS .. "[1]\n" .. self.currentexp .. "/" .. self.levelexp
			)
		else
			inst.components.talker:Say(
				inst:GetDisplayName() ..
					" Lv" ..
						self.currentlevel ..
							"\n" .. STRINGS.MUSHA_ITEM_LUCKY .. "[" .. exp .. "]\n" .. self.currentexp .. "/" .. self.levelexp
			)
		end
	end
end

function Musha_Equipment:SetMaxLevel(maxlevel, extraexp, extrafn)
	self.maxlevel = maxlevel
	self.extraexp = extrafn and extraexp
	self.extrafn = extrafn
end

function Musha_Equipment:OnLoad(data)
	if data ~= nil then
		if data.level ~= nil then
			self.currentlevel = math.clamp(data.level, 1, self.maxlevel)
		end
		if data.exp ~= nil then
			self.currentexp = math.max(data.exp, 0)
		end
	end
end

function Musha_Equipment:OnSave()
	return {exp = self.currentexp, level = self.currentlevel}
end

function Musha_Equipment:InitMushaEquip(equiptype, durable, rate)
	local inst = self.inst

	if equiptype == "weapon" then
		inst:AddTag("musha_weapon")
		inst:AddComponent("finiteuses")
		inst.components.finiteuses:SetMaxUses(durable)
		inst.components.finiteuses:SetPercent(1)
		inst:AddComponent("weapon")
		inst.components.equippable.equipslot = EQUIPSLOTS.HANDS
		inst.components.weapon.attackwear = 2
		inst.components.weapon:SetOnAttack(
			function(inst, attacker, target)
				inst:BuffAttack(attacker, target)
				-- #TODO:
				if inst.components.useableitem ~= nil and inst.components.useableitem.inuse then
					self:DoDelta(-3)
				else
					self:DoDelta(-2)
				end
				if target then
					if target.components.sleeper and target.components.sleeper:IsAsleep() then
						target.components.sleeper:WakeUp()
					end
					if target.components.burnable and target.components.burnable:IsBurning() and not target:HasTag("buzzard") then
						target.components.burnable:Extinguish()
					end
					if target.components.combat and not target:HasTag("companion") then
						target.components.combat:SuggestTarget(attacker)
					end
				end
			end
		)
		self.GetCurrent = function(self)
			return inst.components.finiteuses.current
		end
		self.DoDelta = function(self, delta)
			inst.components.finiteuses:SetUses(math.clamp(self.GetCurrent() + delta, 0, inst.components.finiteuses:GetUses()))
		end
		self.IsBroken = function(self)
			return self.GetCurrent() <= 0
		end
	else
		if equiptype == "armor" then
			inst:AddTag("musha_armor")
			inst.components.equippable.equipslot = EQUIPSLOTS.BODY
			if not MUSHA_SETTINGS.no_armor_limit then
				local function check_limit(inst, data)
					local owner = data.owner
					if
						owner and
							(owner.components.inventory:HasItemWithTag("musha_armor", 1) or
								owner.components.inventory:EquipHasTag("musha_armor"))
					 then
						owner:DoTaskInTime(
							0,
							function()
								owner.components.inventory:DropItem(inst)
							end
						)
					end
				end
				inst:ListenForEvent("onpickup", check_limit)
				inst:ListenForEvent("equipped", check_limit)
			end
		elseif equiptype == "hat" then
			inst:AddTag("musha_hat")
			inst.components.equippable.equipslot = EQUIPSLOTS.HEAD
		end

		inst:AddComponent("armor")
		inst.components.armor:InitCondition(durable, 0)
		inst.components.armor.indestructible = true
		self.GetCurrent = function(self)
			return inst.components.armor.condition
		end
		self.DoDelta = function(_, delta)
			local self = inst.components.armor
			self.condition = math.clamp(self.condition + delta, 0, self.maxcondition)
			inst:PushEvent("percentusedchange", {percent = self:GetPercent()})
			if self.condition <= 0 and self.onfinished then
				self.onfinished()
			end
		end
		self.IsBroken = function(self)
			return self.GetCurrent() <= 0
		end
		inst.components.armor.OnLoad = function(_, data)
			if data.condition ~= nil then
				self:DoDelta(data.condition - inst.components.armor.maxcondition)
			end
		end
		local beattacked = function(_, data)
			if self:IsBroken() then
				return
			end
			if data and data.attacker and data.attacker.components.combat then
				self:DoDelta(-data.attacker.components.combat.defaultdamage)
			end

			if math.random() < 0.5 then
				self:Gain_Exp(1)
			end
		end
		inst:ListenForEvent(
			"equipped",
			function(inst, data)
				inst:ListenForEvent("attacked", beattacked, data.owner)
			end
		)
		inst:ListenForEvent(
			"unequipped",
			function(inst, data)
				inst:RemoveEventCallback("attacked", beattacked, data.owner)
			end
		)
	end

	self:SetFuelRate(rate)
end

return Musha_Equipment
